﻿using System;
using System.Collections.Generic;
using System.Linq;
using Servicios.Business.Exceptions;
using Servicios.Business.Extensions;
using System.Data.Objects.DataClasses;
using System.Data.Objects;

namespace Servicios.Business.Proveedores
{
    public abstract class BusinessEntityProvider<TContext,TEntity> : IDisposable
        where TContext : ObjectContext, new()
        where TEntity : EntityObject, new()
    {
        private TContext _context;
        public TContext Context
        {
            get { return _context; }
            set { _context = value; }
        }

        public BusinessEntityProvider()
        {
            _context =  new TContext();
        }

        public BusinessEntityProvider(TContext context)
        {
            _context = context;
        }

        #region Core Methods

        /// <summary>
        /// Crea un nuevo ObjectEntity para su manipulación en subsiguientes capas
        /// </summary>
        /// <returns></returns>
        public virtual TEntity CreateEntity()
        {
            return new TEntity();
        }

        /// <summary>
        /// Obtiene una lista de Entidad por defecto
        /// </summary>
        /// <returns></returns>
        public virtual List<TEntity> GetAll()
        {
            string eSetName = GetEntitySetName();
            var esql = "SELECT VALUE list FROM " + eSetName + " AS list";
            var objQuery = _context.CreateQuery<TEntity>(esql);
            return objQuery.ToList();
        }

        /// <summary>
        /// Obtención de datos de Entidad por su ID
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        public virtual TEntity GetById(long id)
        {
            string eSetName = GetEntitySetName();
            var esql = "SELECT VALUE RefEntity FROM " + eSetName + " AS RefEntity where RefEntity.ID = " + id;
            try
            {
                var objQuery = _context.CreateQuery<TEntity>(esql);
                return objQuery.FirstOrDefault();
            }
            catch(Exception ex)
            {
                throw new ProviderException("La entidad no presenta ID o no se puede obtener via ID. Considere reescribir el método GetById \n"+ex);
            }
        }

        /// <summary>
        /// Actualización de datos de Entidad que tiene ID
        /// </summary>
        /// <param name="updEntity"></param>
        public virtual void Update(TEntity updEntity)
        {
            string eSetName = GetEntitySetName();
            //válido para IDs
            int id;
            bool HasID = Int32.TryParse(updEntity.EntityKey.EntityKeyValues[0].Value.ToString(), out id);
            if (HasID)
            {
                var entityToUpdate = GetById(id);
                if (entityToUpdate != null)
                {
                    _context.ApplyPropertyChanges(eSetName, entityToUpdate);
                    _context.SaveChanges();
                }
            }
            else
            {
                throw new ProviderException("La entidad no presenta ID, no se puede actualizar. Considere reescribir el método Update");
            }
        }

        /// <summary>
        /// Inserción de datos de Entidad
        /// </summary>
        /// <param name="entity"></param>
        public virtual void Insert(TEntity entity)
        {
            string eSetName = GetEntitySetName();
            _context.AddObject(eSetName,entity);
            _context.SaveChanges();
        }

        /// <summary>
        /// Eliminación de datos de Entidad
        /// </summary>
        /// <param name="entity"></param>
        public virtual void Delete(TEntity entity)
        {
            string eSetName = GetEntitySetName();
            _context.Attach(entity);
            _context.DeleteObject(entity);
            _context.SaveChanges();
        }

        #endregion

        #region Métodos de Ayuda
        protected string  GetEntitySetName()
        {
            var entityName = typeof(TEntity).Name;
            var eSetName = _context.MetadataWorkspace.GetEntitySetFullName(entityName);
            return eSetName;
        }
        #endregion

        #region Implementation of IDisposable
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private bool _disposedValue = false;
        
        protected virtual void Dispose(bool disposing)
        {
            if (!_disposedValue)
            {
                if (disposing)
                {
                    _context.Dispose();
                }
            }
            _disposedValue = true;
        }
        #endregion
    }
}